///************************************************************************
/// <copyrigth>2018-2019 Corporation.All Rights Reserved</copyrigth>
/// <author>tangyingzhong</author>
/// <contact>tangyz114987@outlook.com</contact>
/// <version>v1.0.0</version>
/// <describe>
/// You can use it to do http or https post and get 
///</describe>
/// <date>2019/11/15</date>
///***********************************************************************
#ifndef TYZLIBCURL_H
#define TYZLIBCURL_H

#include <mutex>
#include "curl.h"
#include "easy.h"
#include "DllLoader/DllLoader.h"
#include "ITyzLibCurl.h"

using namespace SystemTool;

struct FtpInfo
{
	// Remote ip address
	std::string strIpAddr;

	// User name
	std::string strUserName;

	// Passsword
	std::string strPassword;

	// PortNo
	int iPortNo;

	FtpInfo()
	{
		Clear();
	}

	void Clear()
	{
		strIpAddr = "";

		strUserName = "";

		strPassword = "";

		iPortNo = 21;
	}

	bool IsEmpty()
	{
		return strIpAddr.empty() || strUserName.empty();
	}
};

class TyzLibCurl :public ITyzLibCurl
{
public:
	typedef void None;
	typedef bool Boolean;
	typedef None* Object;
	typedef int Int32;
	typedef unsigned int UInt32;
	typedef double Real;
	typedef struct curl_slist* HeadList;
	typedef CURLcode RetCode;
	typedef CURL* UrlHandle;
	struct GlobalCurl
	{
		GlobalCurl()
		{
			TyzLibCurl::InitCurl();
		}

		~GlobalCurl()
		{
			TyzLibCurl::DestoryCurl();
		}
	};

public:
	// Construct the TyzLibCurl
	TyzLibCurl();

	// Detructe the TyzLibCurl
	virtual ~TyzLibCurl();

private:
	// Forbid the copy TyzLibCurl
	TyzLibCurl(const TyzLibCurl& other) {	}

	// Forbid the assigment of TyzLibCurl
	TyzLibCurl& operator=(const TyzLibCurl& other) { return *this; }

public:
	// Get the current TyzLibCurl version
	virtual std::string GetCurVersion();

	// Set time out
	virtual void SetTimeout(int iSeconds = 60);

	// Set try count
	virtual void SetTryCount(int iCount = 3);

	// Set upload callback function
	virtual void SetUploadFunc(UploadProgress pUploadFunc = NULL, void* pUserData = NULL);

	// Set download callback function
	virtual void SetDownloadFunc(DownLoadProgress pDownloadFunc = NULL, void* pUserData = NULL);

	// Set head for url
	virtual void SetRequestHead(ContentTypeEnum eContentType = APP_JSON,
		ContentChartSetEnum eCharSet = CHARSET_UTF8);

	// Get the respoend by http or https (pCaPath==NULL : do not verify the certification on server)
	virtual bool Get(std::string strRequestUrl,
		std::string& strResponseData,
		std::map<std::string, std::string>* pRequestMap = NULL,
		const char* pCaPath = NULL);

	// Post the request by https (pCaPath==NULL : do not verify the certification on server)
	virtual bool Post(std::string strRequestUrl,
		std::string strRequestJsonData,
		std::string& strResponseData,
		const char* pCaPath = NULL);

	// Post the request on mode application/x-www-form-urlencoded,(pCaPath==NULL : do not verify the certification on server)
	virtual bool Post(std::string strRequestUrl,
		std::string& strResponseData,
		std::map<std::string, std::string>* pRequestMap = NULL,
		const char* pCaPath = NULL);

	// Upload file (application/octet-stream)
	virtual bool UploadFile(std::string strRequestUrl,
		std::vector<std::string>& LocalFileTable,
		std::string& strRespondData,
		const char* pCaPath = NULL);

	// Download file (application/octet-stream)
	virtual bool DownloadFile(std::string strRequestUrl,
		std::string strLocalSaveFilePath,
		const char* pCaPath = NULL);

	// Ftp connection
	virtual bool FtpConnect(const std::string strRemoteIpAddr,
		const std::string strUserName,
		const std::string strPassword,
		const std::string strPortNo = "21");

	// List ftp directory info (strRemoteDirPath: /Test/12/)
	virtual bool FtpList(const std::string strRemoteDirPath, std::vector<FileInfo>& FileInfoTable);

	// Get file information
	virtual bool GetFtpFileInfo(const std::string strRemoteFilePath, FileInfo& Info);

	// Ftp upload file (strRemoteFilePath: /test)
	virtual bool FtpUpload(const std::string strRemoteDirPath, const std::string strLocalFilePath);

	// Ftp download (strRemoteFilePath: /test/34.txt)
	virtual bool FtpDownload(const std::string strRemoteFilePath, const std::string strLocalDirPath);

	// Ftp upload directory
	virtual bool FtpUploadDir(const std::string strRemoteDirPath, const std::string strLocalDirPath);

	// Ftp download directory
	virtual bool FtpDownloadDir(const std::string strRemoteDirPath, const std::string strLocalDirPath);

	// Get the error std::string
	virtual void GetErrorInfo(int& iErrorCode, std::string& strErrorMsg);

private:
	// Init the curl
	static None InitCurl();

	// Destory the curl
	static None DestoryCurl();

	// Write data (Called by url inner)
	static size_t OnWriteData(void* buffer,
		size_t size,
		size_t nmemb,
		std::string* strData);

	// Progerss call back for download and upload
	static int ProgressCallback(void* pUserData,
		double TotalToDownload,
		double NowDownloaded,
		double TotalToUpload,
		double NowUpload);

	// Get content length
	static size_t GetContentLength(Object pHeader,
		size_t iHeadSize,
		size_t iMem,
		Object pStream);

	// Read data (Called by url inner)
	static size_t OnReadData(void* buffer,
		size_t size,
		size_t nmemb,
		void* lpVoid);

	// Write data (Called by url inner)
	static size_t OnWriteFileData(void* buffer,
		size_t size,
		size_t nmemb,
		void* lpVoid);

private:
	// Initialize the TyzLibCurl
	None Initialize();

	// Destory the TyzLibCurl
	None Destory();

	// Is Ftp curl valid
	Boolean IsFtpCurlValid();

	// Create Curl
	Boolean CreateUrL(UrlHandle& pHandle);

	// Destory Curl
	None DestoryUrL(UrlHandle pHandle);

	// Clear the head list
	None ClearRequeHeadHead(HeadList& hList);

	// Get error info
	std::string GetCurlErrorInfo(RetCode eRetCode);

	// Set error info
	None SetErrorInfo(RetCode eRetCode, std::string strErrorMsg);

	// Is can do the operation
	Boolean IsCanOperate();

	// Parse the file info
	Boolean GetFileInfo(std::string strParentPath, std::string strAllFileInfo, std::vector<FileInfo>& Table);

	// Build remote file path
	Boolean BuildFilePath(std::string strFilePath,
		std::string strDirPath,
		std::string& strFinalFilePath);

	// Get file info 
	bool GetFileInfo(std::string strInfo,std::string& strFileInfo);

	// Get file info
	bool GetFileInfoByName(FileInfo& InInfo, std::string strFileName);

private:
	// Get the disposed status
	inline Boolean GetDisposed() const
	{
		return m_bDisposed;
	}

	// Set the disposed status
	inline None SetDisposed(Boolean bDisposed)
	{
		m_bDisposed = bDisposed;
	}

	// Get the IsInit
	inline static Boolean GetIsInit()
	{
		return m_bIsInit;
	}

	// Set the IsInit
	inline static void SetIsInit(Boolean bIsInit)
	{
		m_bIsInit = bIsInit;
	}

	// Get the ErrorCode
	inline Int32 GetErrorCode() const
	{
		return m_iErrorCode;
	}

	// Set the ErrorCode
	inline void SetErrorCode(Int32 iErrorCode)
	{
		m_iErrorCode = iErrorCode;
	}

	// Get the ErrorMsg
	inline std::string GetErrorMsg() const
	{
		return m_strErrorMsg;
	}

	// Set the ErrorMsg
	inline void SetErrorMsg(std::string strErrorMsg)
	{
		m_strErrorMsg = strErrorMsg;
	}

	// Get the TimeoutS
	inline UInt32 GetTimeoutS() const
	{
		return m_iTimeoutS;
	}

	// Set the TimeoutS
	inline void SetTimeoutS(UInt32 iTimeoutS)
	{
		m_iTimeoutS = iTimeoutS;
	}

	// Get the TryCount
	inline Int32 GetTryCnt() const
	{
		return m_iTryCount;
	}

	// Set the TryCount
	inline void SetTryCnt(Int32 iTryCount)
	{
		m_iTryCount = iTryCount;
	}

	// Get the UplaodFunc
	inline UploadProgress GetUplaodFunction() const
	{
		return m_pUplaodFunc.first;
	}

	// Set the UplaodFunc
	inline void SetUploadFunction(UploadProgress pUplaodFunc)
	{
		m_pUplaodFunc.first = pUplaodFunc;
	}

	//Get the DownloadFunc
	inline DownLoadProgress GetDownloadFunction() const
	{
		return m_pDownloadFunc.first;
	}

	//Set the DownloadFunc
	inline void SetDownloadFunction(DownLoadProgress pDownloadFunc)
	{
		m_pDownloadFunc.first = pDownloadFunc;
	}

	// Get the UplaodFunc user data
	inline Object GetUserData(bool bIsUploaded = true) const
	{
		if (bIsUploaded)
		{
			return m_pUplaodFunc.second;
		}

		return m_pDownloadFunc.second;
	}

	// Set the UserData
	inline void SetUserData(Object pUserData, bool bIsUploaded = true)
	{
		if (bIsUploaded)
		{
			m_pUplaodFunc.second = pUserData;
		}
		else
		{
			m_pDownloadFunc.second = pUserData;
		}
	}

	// Get the HeadList
	inline HeadList GetHeadList() const
	{
		return m_pHeadList;
	}

	// Set the HeadList
	inline void SetHeadList(HeadList pHeadList)
	{
		m_pHeadList = pHeadList;
	}

	// Get the UploadTotalSize
	inline Real GetUploadTotalSize() const
	{
		return m_dUploadTotalSize;
	}

	// Set the UploadTotalSize
	inline void SetUploadTotalSize(Real dUploadTotalSize)
	{
		m_dUploadTotalSize = dUploadTotalSize;
	}

	// Get the FtpHandle
	inline UrlHandle GetFtpHandle() const
	{
		return m_hFtpHandle;
	}

	// Set the FtpHandle
	inline void SetFtpHandle(UrlHandle hFtpHandle)
	{
		m_hFtpHandle = hFtpHandle;
	}

	// Get the IsFtpUpload
	inline Boolean GetIsFtpUpload() const
	{
		return m_bIsFtpUpload;
	}

	// Set the IsFtpUpload
	inline void SetIsFtpUpload(Boolean bIsFtpUpload)
	{
		m_bIsFtpUpload = bIsFtpUpload;
	}

private:
	// Is curl initialized
	static Boolean m_bIsInit;

	// Ftp curl handle
	UrlHandle m_hFtpHandle;

	// Ftp information
	FtpInfo m_FtpInfo;

	// Is Ftp upload
	Boolean m_bIsFtpUpload;

	// Total size of uploading
	Real m_dUploadTotalSize;

	// Head list
	HeadList m_pHeadList;

	// Timeout 
	UInt32 m_iTimeoutS;

	// Try count
	Int32 m_iTryCount;

	// Upload func
	std::pair<UploadProgress, Object> m_pUplaodFunc;

	// Download func
	std::pair<DownLoadProgress, Object> m_pDownloadFunc;

	// Error code
	Int32 m_iErrorCode;

	// Error message
	std::string m_strErrorMsg;

	// Disposed status
	Boolean m_bDisposed;
};

#endif // TYZLIBCURL_H